#define _GNU_SOURCE     # required for NI_NUMERICHOST
#include "libkync.h"
#include <cstring-extension.h>
#include <errno.h>
#include <kerr.h>
#include <libkylog.h>
#include <stdio.h>
#include <string.h>
#include <sdkmarcos.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <unistd.h>

enum cardspec{
    NCSPEC_ALL,
    NCSPEC_UP,
    NCSPEC_DOWN
};

enum cardstat{
    NCSTAT_LINK_UP,
    NCSTAT_LINK_DOWN,
    NCSTAT_ERROR
};

enum cardcfg{
    NCCFG_MAC,
    NCCFG_IPv4,
    NCCFG_IPv6,
    NCCFG_IPv4_MASK,
    NCCFG_IPv6_MASK
};

#define NC_IPv4_SIZE    16
#define NC_MAC_SIZE     18
#define SIOCGIWNAME     0x8B01
#define PCIID_PATH "/usr/share/misc/pci.ids"

static enum cardstat _get_card_stat(const char *nc)
{
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (!sfd)
    {
        klog_err("网卡[%s]状态获取失败：%s\n", nc, strerror(errno));
        return NCSTAT_ERROR;
    }

    struct ifreq stIf;
    strcpy(stIf.ifr_ifrn.ifrn_name, nc);

    if (ioctl(sfd, SIOCGIFFLAGS, &stIf) < 0)
    {
        klog_err("网卡[%s]状态获取失败：%s\n", nc, strerror(errno));
        close (sfd);
        return NCSTAT_ERROR;
    }

    close(sfd);
    if (stIf.ifr_ifru.ifru_flags & IFF_RUNNING)
        return NCSTAT_LINK_UP;

    return NCSTAT_LINK_DOWN;
}

static char **_get_cardlist(enum cardspec spec)
{
    char **cards = NULL;
    size_t cardnum = 0;
#ifdef __linux__
    FILE* fp = fopen("/proc/net/dev", "r");
    ASSERT_NOT_NULL(fp, NULL);
    char buffer[1024];
    while (fgets(buffer, 1024, fp))
    {
        if (strncmp(strskipblank(buffer), "Inter", 5) == 0 || strncmp(strskipblank(buffer), "face", 4) == 0)
            continue;
        int pos = strfirstof(buffer, ':');
        if (pos > 0)
        {
            buffer[pos] = 0;
            strstripblank(buffer);
            if (spec == NCSPEC_UP)
            {
                if (_get_card_stat(buffer) != NCSTAT_LINK_UP)
                    continue;
            }
            else if (spec == NCSPEC_DOWN)
            {
                if (_get_card_stat(buffer) != NCSTAT_LINK_DOWN)
                    continue;
            }
            cardnum ++;
            char **tmp = realloc(cards, sizeof(char*) * cardnum);
            if (!tmp)
            {
                klog_err("内存申请失败：%s\n", strerror(errno));
                goto err_out;
            }
            cards = tmp;
            int index = cardnum - 1;
            cards[index] = malloc(sizeof(char) * (strlen(buffer) + 1));
            if (!cards[index])
            {
                klog_err("内存申请失败：%s\n", strerror(errno));
                goto err_out;
            }
            strcpy(cards[index], buffer);
        }
    }
    char **tmp = realloc(cards, sizeof(char *) * (cardnum + 1));
    if (!tmp)
    {
        klog_err("内存申请失败：%s\n", strerror(errno));
        goto err_out;
    }
    cards = tmp;
    cards[cardnum] = NULL;
    goto out;
#endif  // __linux__
err_out:
#ifdef __linux__
    fclose(fp);
#endif
    while (cardnum)
    {
        free(cards[cardnum - 1]);
        cardnum--;
    }
    SAFE_FREE(cards);
    return NULL;
out:
    fclose(fp);
    return cards;
}

char **_get_nc_cfg(const char *nc, enum cardcfg cfg)
{
    char **res = NULL;
    struct ifreq stIf;
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd < 0)
    {
        klog_err("网卡[%s]信息获取失败：%s\n", nc, strerror(errno));
        return NULL;
    }
    strncpy(stIf.ifr_ifrn.ifrn_name, nc, sizeof(stIf.ifr_ifrn.ifrn_name) - 1);

    switch (cfg)
    {
    case NCCFG_MAC:{
        if (ioctl(sfd, SIOCGIFHWADDR, &stIf) < 0)
        {
            klog_err("网卡[%s]MAC获取失败：%s\n", nc, strerror(errno));
            close(sfd);
            return NULL;
        }
        res = malloc(sizeof(char*));
        if (!res)
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            return NULL;
        }
        res[0] = malloc(sizeof(char) * NC_MAC_SIZE);
        if (!res[0])
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            return NULL;
        }
        snprintf(res[0], NC_MAC_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[0],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[1],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[2],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[3],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[4],
            (unsigned char)stIf.ifr_ifru.ifru_hwaddr.sa_data[5]);
    }break;
    case NCCFG_IPv4:{
        if (ioctl(sfd, SIOCGIFADDR, &stIf) < 0)
        {
            klog_err("网卡[%s]IPv4获取失败：%s\n", nc, strerror(errno));
            close(sfd);
            return NULL;
        }
        res = malloc(sizeof(char*));
        if (!res)
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            return NULL;
        }
        res[0] = malloc(sizeof(char) * NC_IPv4_SIZE);
        if (!res[0])
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            return NULL;
        }
        struct sockaddr_in sin;
        memcpy(&sin, &stIf.ifr_ifru.ifru_addr, sizeof(sin));
        snprintf(res[0], NC_IPv4_SIZE, "%s", inet_ntoa(sin.sin_addr));
    }break;
    case NCCFG_IPv6:{
        struct ifaddrs *ifap, *ifa;
        struct sockaddr_in6 *sa;
        char addr[INET6_ADDRSTRLEN] = {0};
        getifaddrs(&ifap);
        for (ifa = ifap; ifa; ifa = ifa->ifa_next)
        {
            if (ifa->ifa_addr->sa_family == AF_INET6 && !strcmp(ifa->ifa_name, nc))
            {
                sa = (struct sockaddr_in6 *)ifa->ifa_addr;
                getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr,
                            sizeof(addr), NULL, 0, NI_NUMERICHOST);
            }
        }

        res = malloc(sizeof(char*));
        if (!res)
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            freeifaddrs(ifap);
            return NULL;
        }
        res[0] = malloc(sizeof(char) * INET6_ADDRSTRLEN);
        if (!res[0])
        {
            klog_err("内存申请失败:%s\n", strerror(errno));
            close(sfd);
            freeifaddrs(ifap);
            return NULL;
        }

        int i = 0;
        while (addr[i] != '%' && addr[i] != '\0')
            i++;
        addr[i] = '\0';
        
        sprintf(res[0], "%s",addr);

        freeifaddrs(ifap);
    }break;
    default:
        break;
    }
    close(sfd);
    return res;
}

char** kdk_nc_get_cardlist()
{
    return _get_cardlist(NCSPEC_ALL);
}

int kdk_nc_is_up(const char *nc)
{
    if (!nc)
        return -1;
    enum cardstat stat = _get_card_stat(nc);
    if (stat == NCSTAT_LINK_UP)
        return 1;
    else if (stat == NCSTAT_LINK_DOWN)
        return 0;
    return -1;
}

char** kdk_nc_get_upcards()
{
    return _get_cardlist(NCSPEC_UP);
}

char* kdk_nc_get_phymac(const char *nc)
{
    if (!nc)
        return NULL;
    
    char **maclist = _get_nc_cfg(nc, NCCFG_MAC);
    if (!maclist)
        return NULL;
    char *mac = maclist[0];
    free(maclist);
    return mac;
}

char* kdk_nc_get_private_ipv4(const char *nc)
{
    if (!nc)
        return NULL;
    
    char **ipv4list = _get_nc_cfg(nc, NCCFG_IPv4);
    if (!ipv4list)
        return NULL;
    char *ipv4 = ipv4list[0];
    free(ipv4list);
    return ipv4;
}

char* kdk_nc_get_private_ipv6(const char *nc)
{
    if (!nc)
        return NULL;

    char **ipv6list = _get_nc_cfg(nc, NCCFG_IPv6);
    if (!ipv6list)
        return NULL;
    char *ipv6 = ipv6list[0];
    free(ipv6list);
    return ipv6;
}

int kdk_nc_is_wireless(const char *nc)
{
    if (!nc)
        return -1;
    char buffer[2 * IFNAMSIZ + 1];
    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
        return -1;
    memset(buffer, 0, sizeof(buffer));
    strncpy(buffer, nc, sizeof(buffer));
    return ioctl(fd, SIOCGIWNAME, &buffer) == 0 ? 1 : 0;
}

int kdk_nc_get_vendor_and_product(const char *nc, char *vendor, char *product)
{
    if (!nc)
        return -1;
    char filename[64] = "0";
    sprintf(filename, "/sys/class/net/%s/device", nc);
    if (0 != access(filename, F_OK))
        return -1; //非物理网卡，可能是逻辑网卡如 lo: 本地环回
    FILE *fp = NULL;
    char vendorId[64] = "0";
    char productId[64] = "0";
    strcpy(vendorId, filename);
    strcpy(productId, filename);
    //读厂家id
    if (NULL != (fp = fopen(strcat(vendorId, "/vendor"), "r")))
    {
        fgets(vendorId, sizeof(vendorId), fp);
        fclose(fp);
    }
    //读设备id
    if (NULL != (fp = fopen(strcat(productId, "/device"), "r")))
    {
        fgets(productId, sizeof(vendorId), fp);
        fclose(fp);
    }

    long u[4]; // 0,1存储网卡的厂家和设备ID，2，3存储pci.ids文件读到的ID
    memset(u, 0, sizeof(u));
    sscanf(vendorId, "%lx", &u[0]);
    sscanf(productId, "%lx", &u[1]);

    char line[512] = "0";
    if (NULL != (fp = fopen(PCIID_PATH, "r")))
    {
        long unsigned int level = 0;
        while (fgets(line, sizeof(line), fp))
        {
            // 忽略空行和注释
            if (strlen(line) == 0 || line[0] == '#')
                continue;

            //行前无制表符代表厂家id，一个制表符代表设备id
            level = 0;
            while (level < strlen(line) && line[level] == '\t')
                level++;
            //获取厂家名称
            if (0 == level)
            {
                sscanf(line, "%lx", &u[2]);
                if (u[0] == u[2])
                    strcpy(vendor, line + level + 6);
            }
            //在对应的厂家id下查找设备id
            if (1 == level && u[0] == u[2])
            {
                sscanf(line, "%lx", &u[3]);
                if (u[1] == u[3])
                {
                    strcpy(product, line + level + 6);
                    break;
                }
            }
        }
    }
    return 0;
}

inline void kdk_nc_freeall(char **list)
{
    if (! list)
        return;
    size_t index = 0;
    while (list[index])
    {
        free(list[index]);
        index ++;
    }
    free(list);
}